package com.jweb.framework.ws.soap.axiom;
import java.io.ByteArrayOutputStream;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;

import org.apache.axiom.om.OMDataSource;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.ds.ByteArrayDataSource;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.xml.transform.StaxResult;

import com.jweb.framework.core.util.XMLUtils;

/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：
 */
public class NonCachingPayload extends Payload {
	
	NonCachingPayload(SOAPBody axiomBody , SOAPFactory axiomFactory) {
		super(axiomBody, axiomFactory);
	}
	
	public Result getResultInternal() {
		return new StaxResult(new DelegatingStreamWriter());
	}
	
	protected XMLStreamReader getStreamReader(OMElement payloadElement){
		return payloadElement.getXMLStreamReaderWithoutCaching();
	}
	
	private class DelegatingStreamWriter implements XMLStreamWriter {
		
		private final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
		
		private final XMLStreamWriter delegate ;
		
		private QName name ;
		
		private String encoding = "UTF-8";
		
		private int elementDepth = 0 ;
		
		private boolean payloadAdded = false ;
		// NamespaceContext：用于只读XML名称空间上下文处理的接口。
		private NamespaceContext namespaceContext ;
		
		private DelegatingStreamWriter(){
			try {
				this.delegate = XMLUtils.createXMLWriter(this.baos , this.encoding , false);
			} 
			catch (XMLStreamException e) {
				throw new AxiomSoapBodyException("Could not determine payload root element" , e);
			}
		}
		
		public void writeStartDocument() throws XMLStreamException {
			
		}
		
		public void writeStartDocument(String version) throws XMLStreamException {
			
		}
		
		public void writeStartDocument(String encoding , String version) throws XMLStreamException {
			this.encoding = encoding ;
		}
		
		public void writeStartElement(String localName) throws XMLStreamException {
			if(name == null){
				name = new QName(localName);
			}
			elementDepth++;
			delegate.writeStartElement(localName);
		}
		
		public void writeStartElement(String namespaceURI , String localName) throws XMLStreamException {
			
			if(name == null) {
				name = new QName(namespaceURI , localName);
			}
			elementDepth++ ;
			
			String str = (this.namespaceContext == null) ? null : this.namespaceContext.getPrefix(namespaceURI);
			if(str != null){
				this.delegate.writeStartElement(str , localName, namespaceURI);
				if(str.length()>0){
					this.delegate.writeNamespace(str, namespaceURI);
					this.delegate.setPrefix(str, namespaceURI);
				}
				else {
					this.delegate.writeDefaultNamespace(namespaceURI);
					this.delegate.setDefaultNamespace(namespaceURI);
				}
			}
			else{
				this.delegate.writeStartElement(namespaceURI , localName);
			}
			
		}
		
		public void writeStartElement(String prefix , String localName , String namespaceURI) throws XMLStreamException {
			if(name == null){
				name = new QName(namespaceURI , localName , prefix);
			}
			elementDepth++ ;
			delegate.writeStartElement(prefix, localName, namespaceURI);
		}
		
		public void writeEndElement() throws XMLStreamException {
			elementDepth-- ;
			delegate.writeEndElement();
			addPayload();
		}

		private void addPayload() throws XMLStreamException {
			if(elementDepth <= 0 && !payloadAdded){
				delegate.flush();
				if(baos.size() > 0){
					byte[] buf = baos.toByteArray();
					OMDataSource dataSource = new ByteArrayDataSource(buf, encoding);
					OMNamespace namespace = getAxiomFactory().createOMNamespace(name.getNamespaceURI(), name.getPrefix());
					OMElement payloadElement = getAxiomFactory().createOMElement(dataSource, name.getLocalPart() , namespace);
					getAxiomBody().addChild(payloadElement);
					payloadAdded = true ;
				}
			}
		}
		
		public void writeEmptyElement(String localName) throws XMLStreamException {
			if(name == null){
				name = new QName(localName);
			}
			delegate.writeEmptyElement(localName);
			addPayload();
		}
		
		public void writeEmptyElement(String namespaceURI , String localName) throws XMLStreamException {
			if(name == null){
				name = new QName(namespaceURI , localName);
			}
			delegate.writeEmptyElement(namespaceURI, localName);
			addPayload();
		}
		
		public void writeEmptyElement(String prefix , String localName , String namespaceURI) throws XMLStreamException {
			if(name == null){
				name = new QName(namespaceURI , localName , prefix);
			}
			delegate.writeEmptyElement(prefix , localName , namespaceURI);
			addPayload();
		}
		
		public void writeEndDocument() throws XMLStreamException {
			elementDepth = 0 ;
			delegate.writeEndDocument();
			addPayload();
		}
		
		public void close() throws XMLStreamException {
			addPayload();
			delegate.close();
		}
		
		public void flush() throws XMLStreamException {
			delegate.flush();
		}
		
		public NamespaceContext getNamespaceContext(){
			return this.namespaceContext ;
		}
		
		public String getPrefix(String uri) throws XMLStreamException {
			return delegate.getPrefix(uri);
		}
		
		public Object getProperty(String name) throws IllegalArgumentException {
			return delegate.getProperty(name);
		}
		
		public void setDefaultNamespace(String uri) throws XMLStreamException {
			delegate.setDefaultNamespace(uri);
		}
		
		public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
			this.namespaceContext = context ;
		}
		
		public void setPrefix(String prefix , String uri) throws XMLStreamException {
			delegate.setPrefix(prefix, uri);
		}
		
		public void writeAttribute(String localName , String value) throws XMLStreamException {
			delegate.writeAttribute(localName, value);
		}
		
		public void writeAttribute(String namespaceURI , String localName , String value) throws XMLStreamException {
			delegate.writeAttribute(namespaceURI, localName , value);
		}
		
		public void writeAttribute(String prefix , String namespaceURI , String localName , String value) throws XMLStreamException {
			delegate.writeAttribute(prefix , namespaceURI , localName , value);
		}
		
		public void writeCData(String data) throws XMLStreamException {
			delegate.writeCData(data);
		}
		
		public void writeCharacters(char[] text , int start , int len) throws XMLStreamException {
			delegate.writeCharacters(text, start, len);
		}
		
		public void writeCharacters(String text) throws XMLStreamException {
			delegate.writeCharacters(text);
		}
		
		public void writeComment(String data) throws XMLStreamException {
			delegate.writeComment(data);
		}
		
		public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException {
			delegate.writeDefaultNamespace(namespaceURI);
		}
		
		public void writeDTD(String dtd) throws XMLStreamException {
			delegate.writeDTD(dtd);
		}
		
		public void writeEntityRef(String name) throws XMLStreamException{
			delegate.writeEntityRef(name);
		}
		
		public void writeNamespace(String prefix , String namespaceURI) throws XMLStreamException {
			delegate.writeNamespace(prefix, namespaceURI);
		}
		
		public void writeProcessingInstruction(String target) throws XMLStreamException {
			delegate.writeProcessingInstruction(target);
		}
		
		public void writeProcessingInstruction(String target , String data) throws XMLStreamException {
			delegate.writeProcessingInstruction(target , data);
		}
		
		
	}
	

}